/*
 * @Author: ChenBlue
 * @Date: 2021-07-03 16:17:54
 * @LastEditTime: 2021-07-04 21:11:19
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: \c训练\snake.c
 */
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#if _WIN32
#include <windows.h>
#elif
#include <unistd.h>
#endif
#include <conio.h>
#include <process.h>
#include <malloc.h>

typedef struct node {
    int x;
    int y;
    struct node * before;
    struct node * after;
}Node;

//全局变量的定义
static int snake_len = 2;
static Node *head = NULL;
static Node *tail = NULL;
static int dir = 4; //1 上 2 右 3 下 4 左
static int fruit_x = 0;
static int fruit_y = 0;

//函数声明
static void Maps();
static void Init_snake();
static void Setxy(int x, int y);
static void Draw_snake();
void Scan_keyboard (void *a);
static void Snake_move();
static void Create_fruit();
static void del();
static int Eat_fruit();
static void Snake_up();
static void Erase_snake();
static int GameOver();

void main(void)
{
    #if _WIN32
    _beginthread(Scan_keyboard,0,NULL); //开启键盘扫描多线程
    #elif
    #endif
    del();   //清除光标
    srand((unsigned)time(NULL)); //种下随机数种子
    Init_snake();
    Maps();
    Create_fruit();
    while(1)
    {
        Erase_snake();
        Snake_move();
        if(Eat_fruit())
            Snake_up();
        if(GameOver())
            break;
        Draw_snake();
        Sleep(1000);
    }
    Setxy(0,18);
    printf("Game Over\n");
    system("pause");
}

static int GameOver()
{
    int result = 0;
    Node * temp = head->after;
    if (head->x <= 0 || head->x >= 21 || head->y <=0 || head->y >= 17)
    {
        result = 1;//撞墙
    }
    for (int i = 0; i < snake_len - 1; i++)
    {
        if(head->x == temp->x && head->y == temp->y)
        {
            result = 1;
            break;
        }
        temp = temp->after;
    }
    return result;
}

static void Snake_up()
{
    Node* temp = (Node *)malloc(sizeof(Node));
    if(tail->x == tail->before->x)
    {
        temp->x = tail->x;
        temp->y = 2 * tail->y - tail->before->y;
    }
    if (tail->y == tail->before->y)
    {
        temp->x = 2 * tail->x - tail->before->x;
        temp->y = tail->y;
    }
    
    temp->after = head;
    temp->before = tail;
    tail->after = temp;
    head->before = temp;
    tail = temp;
    snake_len++;
    //free(temp);
    Create_fruit();
}

static int Eat_fruit()
{
    int eat;
    if(head->x == fruit_x && head->y == fruit_y)
        eat = 1;
    else
        eat = 0;
    return eat;
}

static void Create_fruit()
{
    Node * temp = head;

fruit:
    fruit_x = rand() % 20 + 1;
    fruit_y = rand() % 16 + 1;
    for (int i = 0; i < snake_len; i++)
    {
        //如果随机出现再蛇身上
        if(fruit_x == temp->x && fruit_y == temp->y)
            goto fruit;
        temp = temp->after;
    }
    Setxy(fruit_x,fruit_y);
    printf("O");
}

static void Snake_move()
{
    Node* temp = NULL;
    switch (dir)
    {
    case 1:
        tail->x = head->x;
        tail->y = head->y - 1;
        break;
    case 2:
        tail->x = head->x + 1;
        tail->y = head->y;
        break;
    case 3:
        tail->x = head->x;
        tail->y = head->y + 1;
        break;
    case 4:
        tail->x = head->x - 1;
        tail->y = head->y;
        break;

    default:
        break;
    }
    temp = tail;
    tail = tail->before;
    head = temp;
}

static void Erase_snake()
{
    Node* temp = head;
    for (int i = 0; i < snake_len; i++)
    {
        Setxy(temp->x,temp->y);
        printf(" ");
        temp = temp->after;
    }
}

static void Draw_snake()
{
    Node* temp = head->after;
    //画出蛇的头部
    Setxy(head->x,head->y);
    printf("@");
    //蛇身
    for (int i = 0; i < snake_len - 1; i++) {
        Setxy(temp->x,temp->y);
        printf("#");
        temp = temp->after;
    }
}

static void Maps()
{
    printf("######################\n");//22
    for(int i = 0;i < 16; i++)
        printf("#                    #\n");
    printf("######################\n");
}

static void Init_snake()
{
    head = (Node *)malloc(sizeof(Node));
    tail = (Node *)malloc(sizeof(Node));
    head->x = 9;
    head->y = 8 ;
    head->after = tail;
    head->before = tail;
    tail->x = 10;
    tail->y = 8 ;
    tail->after = head;
    tail->before = head;
}

static void Setxy(int x, int y)  //x,y为光标座标
{
   COORD coord = {x, y};
   SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
}

#if _WIN32
void Scan_keyboard (void *a)
{
    int flag;

    while(1)
    {
       if ( kbhit() )  //判断有键按下时才执行下面操作
        {
            flag = getch();  //用flag来获取键值

            if (flag==224)
                flag = getch(); //值为224是方向键，要再次获取才行
            if (flag == 72 && dir != 3) dir = 1; //方向键为“上”让dir=1.
            if (flag == 80 && dir != 1) dir = 3; //方向键为“下”让dir=3.
            if (flag == 75 && dir != 2) dir = 4; //方向键为“左”让dir=4.
            if (flag == 77 && dir != 4) dir = 2; //方向键为“右”让dir=2.
        }
        Sleep(100);
    }
    _endthread();
}
#elif
#endif

static void del()
{
    HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
    CONSOLE_CURSOR_INFO cci;
    GetConsoleCursorInfo(hOut, &cci);
    cci.bVisible = FALSE;
    SetConsoleCursorInfo(hOut, &cci);
}

